feat(desktop+acp): spawn a harness per (agent, community) pair at GUI startup — warm sockets, lazy LLM pool#2122
Conversation
wesbillman
left a comment
There was a problem hiding this comment.
Requesting changes on Wes's behalf after full review in Buzz. The architecture is sound — the lazy-pool state machine, pair receipts, and replace-before-spawn seam are well-built and well-tested — but four concrete issues need to land first:
- CI is red. Desktop Core fails
desktop-tauri-clippywith 3 errors:ManagedAgentRuntimeTargetis never constructed (dead type inruntime_types.rs— no command takes it),write_agent_pid_fileis never used (its last caller was removed; keep it for migration or delete it), and amanual_ignore_case_cmplint inobserver_lifecycle_key(useeq_ignore_ascii_case). - Members-sidebar badge regression.
MembersSidebargained arelayUrl?prop, butChannelScreen— its only call site — never passes it, somanagedAgentRuntimeis alwaysundefinedand every managed agent now renders a visible "Unavailable" badge regardless of actual state (previously an sr-only status). Wire the prop through, or don't render the fallback badge when runtime state is unknown. - Lane D battery isn't in the diff. Only the fixtures shipped (
fake-acp-agent.mjs,twoRelayHarness.ts); nothing importsTwoRelayHarness, so the "integration matrix incl. native replacement probe" described in the PR body isn't in this PR. Either include the specs or state explicitly that they're a follow-up — as merged, this is dead test code. reconcile_managed_agent_runtimesignoresstart_on_app_launch. The launch-restore path filters on it (restore.rs:158), but the new startup reconcile — called unconditionally fromAppShell— spawns every local agent in every membership community regardless of that setting. If "every agent, everywhere" deliberately supersedes the setting, say so and reconcile the two paths; right now an agent the user opted out of auto-start gets started anyway.
What's good, for the record: the PoolLifecycle attempt-token design (stale wakes can't clobber a newer pool; abandoned pools are shut down on both the send-fail path and the post-loop drain), fail-closed receipt replacement (receipt removed only after confirmed exit, tested via an injectable seam), a single canonical relay-URL normalizer explicitly kept separate from the NIP-42 AUTH comparison, and documented lock discipline on managed_agent_runtime_transition. Re-review will be quick once the above land.
Reviewed-by: Brain (Buzz agent) on behalf of Wes
|
Thanks for the thorough review, Wes. All four items are addressed at the current head
Since your review, the branch also picked up two plain merge commits of CI was fully green at |
ed55dcd to
f435d08
Compare
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
restore_managed_agents_on_launch spawned every auto-start pair with lazy=false, booting an eager LLM child on every ordinary launch and silently reintroducing N idle brains — the exact idle cost the pair runtime is meant to avoid. It also decided "untracked" from receipts alone, so a startup reconcile that spawned the same pair during the Phase A window (before restore takes the transition lock) could be killed and replaced eager, flipping the pair's laziness on a race. F1: spawn restore children lazy=true, matching startup reconcile and manual start. Eager on restore buys nothing — a crashed mid-turn session is not resumed by an eager child; the next mention wakes a lazy one just as well. F2: before terminate-and-respawn, check the live process map for an already-tracked live child at this exact pair key and skip if present, mirroring start_pair's live-child guard. Leaves a concurrently reconciled child untouched instead of killing and replacing it. Live restore/reconcile-race integration coverage (real process table + AppHandle) is deferred to the Lane D battery; the inlined guard is not unit-testable in isolation without an AppHandle. Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Pair-scoped stop_managed_agent_runtime removed the runtime from the map before fallible terminate/wait and bailed with ?, leaving a live child untracked (and its receipt gone) on failure — the same invisible-orphan class the agent-wide stop path already handles. On terminate/wait failure, reinsert the runtime and return the error so the pair stays visible and stoppable. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Complete the pair-runtime fix plan so each community's UI tells the truth about its own (agent, community) pair: - Summary scoping: build_managed_agent_summary resolves the viewed workspace's pair key (relay pin, else active workspace relay) and looks up status/pid/log and needs_restart by exact pair key — an agent running only in another community now reads as stopped here, and restart drift is hashed against the pair's own relay instead of the currently viewed workspace's, so community switches no longer flag spurious restarts. - Pair-scoped stop: the user-facing stop_managed_agent command stops only the active workspace's pair via the new stop_managed_agent_workspace_pair (extracted per-pair teardown with reinsert-on-failure), clearing only that pair's session cache; delete and config-restart flows keep draining every pair through stop_managed_agent_process. The auto-restart policy now bounces only the drifted pair for the viewed community (safety comment updated). - Reconcile bootstrap: rename discover_agent_membership to probe_agent_relay_access and gate spawn eligibility on the bounded authenticated probe succeeding rather than kind-39002 membership presence, which cannot exist before a harness first connects and so could never bootstrap a new pair; the reconcile also skips communities whose relay conflicts with an explicit per-record pin. - Post-create bootstrap: creating a local agent fires an idempotent reconcile across all configured communities so the new agent gets a lazy pair everywhere immediately instead of at the next launch; the E2E bridge answers reconcile_managed_agent_runtimes with an empty set so Playwright runs don't throw. Tests: pure pair-key resolution (resolve_workspace_pair_key) covering unpinned per-workspace keys, pin precedence, canonicalization, and invalid pubkeys; exact-pair stop selection; and reconcile pin filtering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Address the cross-agent review of the pair-runtime rework (b469dc1) and finish the plan's Phase 2/3 gaps so every community's UI tells the truth about its own (agent, community) pair and bootstraps reliably. Silent functional breaks: - Session-config bridge: put_agent_session_config no longer requires a relay_url arg the frontend never passed (which silently stopped live-captured model/config from reaching the cache). It reads the pair relay from the harness-attached payload relayUrl (buzz-acp now rides it in session_config_captured, like the lifecycle frames), falling back to the record's effective relay for older harnesses. - Reconcile degradation: a community whose relay URL cannot form a pair key now yields a Failed row (keyed by the raw requested URL) instead of ?-aborting the whole batch and dropping every other community's row. - Pair-status matching: findManagedAgentRuntime canonicalizes the stored community URL (localhost->127.0.0.1, default ports, trailing slash) to match backend-canonical rows, so the pair menu/badge no longer show a running ws://localhost:3000 pair as stopped after a list refetch. buzz-acp robustness: - Gate the lazy Failed-state retry-deadline sleep on pending work so a drained queue after a failed wake no longer busy-spins the select loop. - Drain wake tasks gracefully on shutdown (fire the watch, 30s timeout, abort only as a backstop) so a mid-initialize pool reaps its own partially-spawned agents instead of leaking them via best-effort Drop. Desktop lifecycle + perf: - Shutdown drains every tracked pair per record (not just the first), so a multi-community agent gets the graceful SIGTERM->2s->SIGKILL fan-out. - list_managed_agent_runtimes loads personas/global config once outside the locks and skips the store rewrite when no runtime exited. - reconcile runs its blocking start_pair loop on spawn_blocking. Feedback's substantive gaps (Phase 2): - Members-sidebar search-add routes a local managed agent through attachManagedAgentToChannel (membership + ensure the active community's pair) instead of a bare membership add that leaves it deaf. - Runtime reconciliation is now incremental and retrying: keyed by canonical relay URL, it reconciles a newly configured relay as soon as it appears (no longer coupled to the add flow also switching communities) and retries a failed relay with a capped backoff (5s/30s/2m) rather than giving up until the next switch or relaunch. Docs + tests (Phase 3): - Document that auto-start gating on reconcile is proactive fan-out policy, not a correctness prerequisite; refresh the stale attach comment to note the status check and start are pair-scoped to the active community. - Unit tests: pure reconciliation planning (pending/classify/backoff) and the malformed-URL Failed-row degradation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
hooks.ts crossed the 1000-line file-size ceiling (1002 lines), failing the pre-push desktop-check gate. Move the team query/mutation hooks and teamsQueryKey into teamHooks.ts and re-export them from hooks.ts so existing importers are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f435d08 to
481cb61
Compare
Pair-scoped stop_managed_agent_runtime removed the runtime receipt without any termination attempt when no runtime was tracked at the pair key. If a valid prior-session receipt still pointed at a live child (the crash-recovery window for a non-auto-start agent), this stop left the harness running yet erased the one artifact that sweeps and terminate_untracked_pair_runtime use to find the orphan, so a follow-up start would then spawn a duplicate harness for the same pair. That was asymmetric with the tracked path, which keeps the receipt on disk until a stop actually succeeds. Route the untracked case through terminate_untracked_pair_runtime before removing the receipt: it terminates a live child from a valid receipt and deletes the receipt only after the child exits, so on failure the receipt stays on disk — restoring the tracked path's keep-until-success invariant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Tyler <109685178+tlongwell-block@users.noreply.github.com> Signed-off-by: Tyler <109685178+tlongwell-block@users.noreply.github.com>
The per-record relay_url pin predates workspace-scoped runtimes. Honoring it made pinned agents silently skip fan-out to other communities (the bug Tyler hit live). Instead of migrating/clearing stored pins, ignore them: - effective_agent_relay_url() now always returns the workspace relay; the stored pin is parsed and persisted untouched, so old records read identically and rollback to a pin-honoring build is safe. - reconcile_managed_agent_runtimes drops the record_allows_relay filter: every start_on_app_launch agent fans out to every community. - Spawn-hash/pair-key docs and tests updated: workspace relay changes trip the hash even for pinned records; editing the ignored pin does not. - Relay URL editor field removed from the agent edit dialog; relayUrl is never submitted so the stored value is preserved. Part of #2122. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
…ad-lifecycle * origin/main: fix(desktop): strip GIF metadata extensions before upload (#2425) feat(desktop): gate sign-out behind key backup + typed confirmation (#2424) ci(desktop): add signed macOS canary build (#2419) feat(desktop+acp): spawn a harness per (agent, community) pair at GUI startup — warm sockets, lazy LLM pool (#2122) Show team count separately in channel template rows (#2404) fix(dev): restore shared worktree identity from keyring (#2400) fix(onboarding): skip community profile setup for existing relay members (#2300) fix(mobile): preserve and display sidebar section icons (#2403) Unify sidebar chrome across themes (#2380) feat(media): add S3-truth per-community storage sweep (#2044)
…obile-releasing Co-authored-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co> * origin/main: fix(desktop): strip GIF metadata extensions before upload (#2425) feat(desktop): gate sign-out behind key backup + typed confirmation (#2424) ci(desktop): add signed macOS canary build (#2419) feat(desktop+acp): spawn a harness per (agent, community) pair at GUI startup — warm sockets, lazy LLM pool (#2122) Show team count separately in channel template rows (#2404) fix(dev): restore shared worktree identity from keyring (#2400) fix(onboarding): skip community profile setup for existing relay members (#2300) fix(mobile): preserve and display sidebar section icons (#2403) Unify sidebar chrome across themes (#2380) feat(media): add S3-truth per-community storage sweep (#2044) feat(relay): log NIP-98 pubkey attribution on HTTP bridge requests (#2206) Co-authored-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co> # Conflicts: # RELEASING.md
…ad-lifecycle * origin/main: fix(desktop): strip GIF metadata extensions before upload (#2425) feat(desktop): gate sign-out behind key backup + typed confirmation (#2424) ci(desktop): add signed macOS canary build (#2419) feat(desktop+acp): spawn a harness per (agent, community) pair at GUI startup — warm sockets, lazy LLM pool (#2122) Show team count separately in channel template rows (#2404) fix(dev): restore shared worktree identity from keyring (#2400) fix(onboarding): skip community profile setup for existing relay members (#2300) fix(mobile): preserve and display sidebar section icons (#2403) Unify sidebar chrome across themes (#2380) feat(media): add S3-truth per-community storage sweep (#2044) Signed-off-by: npub1dpf98sl35hm9k65t8h6cvh5n6knn2msugh5k5nmysxwnw5wlh7uqn2wgp4 <685253c3f1a5f65b6a8b3df5865e93d5a7356e1c45e96a4f64819d3751dfbfb8@sprout-oss.stage.blox.sqprod.co>
…e canonical runtime key Since block#2122, spawn paths passed ManagedAgentRuntimeKey.relay_url (the canonical identity form) to spawned children as BUZZ_RELAY_URL. The canonicalizer folds every loopback spelling to 127.0.0.1, but the relay derives the community boundary from the Host header, so a community configured as ws://localhost:3000 had its UI post into the localhost:3000 community while every agent connected to the 127.0.0.1:3000 community. Agents discovered zero channels, logged 'no channel subscriptions resolved - agent will sit idle', and silently never answered mentions. Per the contract documented on buzz_core::relay::normalize_relay_url ('Connection code may retain the configured URL; this canonical form is for identity'), keep the canonical key for identity, receipts, and log paths, and dial the caller-supplied requested URL: - spawn_agent_child dials the relay_url parameter verbatim; the runtime key remains identity-only - start_managed_agent_process, start_pair, and launch restore pass their already-resolved requested URL instead of key.relay_url - reconcile_managed_agent_runtimes forwards the requested community URL to start_pair instead of the canonical form - probe_agent_relay_access probes over the requested URL so the HTTP probe hits the same community the child will join - start_pair now reports requested_relay_url in its status rows, and the Active Communities settings card prefers requestedRelayUrl for start/stop/restart actions so a manual restart re-dials the configured URL rather than the canonical echo No new automated test covers the spawn wiring itself (it forks a real child process); verified with cargo test (1560 passed), clippy, fmt, tsc, and biome. Fixes block#2444 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Lewis Wang <lewis.wang@plusalsostudios.com.au>
Every agent, every community, the moment your GUI starts
On GUI startup, Desktop spawns an ACP harness per (agent, community) pair — every managed agent, in every community it belongs to. Warm authenticated sockets with narrow mention-scoped subscriptions; lazy LLM pool (the agent's brain wakes on the first message addressed to it, everything arriving during wake is held). Idle cost ~5 MB dirty per harness, zero tokens.
Why: mobile can publish a mention into any community's relay but can't spawn a harness on your desktop. Navigation-gated availability made reachability an accident of where the GUI was looking. This makes it a property of the agent — and kills the cold-mention race by construction (the summoning message is always already captured by a warm socket).
Not a new identity model: one keypair, one persona, one 30177 config per agent. Harnesses start only where the agent is already a member.
Structure (four lanes, merged in order)
ddcbb4321, Mari): process map, PID receipts, logs, session cache, per-pair workdirs re-keyed from pubkey toManagedAgentRuntimeKey; fail-closed replace-before-spawn seam for restart (receipt removed only after confirmed exit); bounded-concurrency startup; per-pair stop/restart.eccf18cd7, Wren): opt-in--lazy-pool/BUZZ_ACP_LAZY_POOL(eager remains default); relay auth/subscriptions/presence before any ACP child; exactly-one async wake on accepted flushable work; queued dispatch after Ready; bounded 5s→5m retry; stale/duplicate wake rejection;managed_agent_runtime_lifecycleobserver frames (listening/waking/ready/failed); explicit shutdown cleanup.e29aa1673, Max): pair-scoped runtime state, lifecycle presence in UI, settings per-pair controls.e0ca4dd33, Perci): integration matrix incl. native replacement probe (old PID dead, exactly one live process + one receipt).Verification
Each lane independently reviewed (line review + delta-confirms + mutation-tested unit tests + live native probes; ledger in the build thread). At the integrated head
0f313d2ed(post merge of main31090b186):cargo fmt --all --checkclean, fullbuzz-acpsuite 541 unit + 9 lifecycle, desktop tauri lib 1484 pass,tsc --noEmitclean, desktop unit 3185 pass, all pre-commit/pre-push hooks green.Known follow-ups (flagged, not in this PR)
to_startfilter (correct while restore is record-level, one relay per record); the multi-relay follow-up must key-scope that filter or add the map check to the replacement helper.Redteam + review fix round (head
060c100a8)Seven-lane redteam at
0f313d2ed(2 PASS: crypto/lifecycle-spoof defense verified with live probes; real-native-process replacement/poison battery held) plus Wes review produced four fix lanes, all merged and re-gated:lazy=true(no idle brains on ordinary launch); Phase B takes a live-map guard mirroringstart_pair, so a concurrent reconcile's live child is skipped, never terminated (SpawnOutcome::{Spawned,Skipped,Failed}).BUZZ_MANAGED_AGENT_START_NONCE) carried in every signed lifecycle frame and compared before mutation — replayed genuine frames from a prior harness generation are rejected even while a replacement child is alive (also zeroes the malicious-relay replay ceiling);list_managed_agent_runtimesreaps exited pairs under the transition/store locks and emits explicit Stopped (no stale "Here" on a dead PID); reconcile carries the recordupdated_atfreshness boundary so an intervening user Stop/edit wins; reconcile filters onstart_on_app_launch— "every agent everywhere" means every auto-start agent everywhere; an explicit opt-out survives. Plus review hygiene: 64-hex pubkey validation/canonicalization inManagedAgentRuntimeKey,eq_ignore_ascii_case, deadManagedAgentRuntimeTargetand legacywrite_agent_pid_fileremoved.ChannelScreenwires the active community relay intoMembersSidebar— pair truth when known, legacy Running/Stopped when unknown, no false "Unavailable".BUZZ_E2E_AGENTS_EVERYWHERE=1, own config) drives two isolated local relays with the same agent identity: zero ACP subprocesses before mention, idle RSS/process counts recorded, exactly one correctly-isolated ACK per relay, no owned PIDs after teardown. Excluded from ordinary smoke runs.Gates at the integrated head (same shell as
git rev-parse HEAD=060c100a8, post merge of main768cd4dbe):cargo fmt --all --checkclean; clippy-D warningsfor desktop tauri (all targets) and buzz-acp; buzz-acp suite 542; desktop tauri 1486 + 3;tsc --noEmit; desktop unit 3190/3190; production build; smoke acceptancechannels.spec.ts:2898(members sidebar respawn) passed;pnpm checkfile-size/px/pubkey gates clean; pre-push full hook suite green.Round-2 fix: agent-wide lifecycle fan-out (head after
060c100a8)Max's independent review at
060c100a8found the legacy agent-wide paths never fanned out to pair runtimes. Fixed in a three-commit lane (Wren, verified independently by Max and Eva, accepted at exact tip):start_pairpath (fresh generation nonces, receipts, live-map guard) — never the scalar/eager legacy start. Candidate pre-filters now inspect live pair runtimes instead of the obsoleterecord.runtime_pid(always None under pair keying — the old pre-check would have silently disabled config-change restarts). The factored pair preflight retains the persona re-snapshot for 30177 relay sync, matching the legacy preflight contract.Additional named fast-follows from this round:
Documented limitations (ruled, not fixed here)